package com.itjeffrey.autocode.quartz.schedule;

import com.itjeffrey.autocode.constant.SysConstant;
import com.itjeffrey.autocode.enums.ExceptionEnum;
import com.itjeffrey.autocode.exception.ACException;
import com.itjeffrey.autocode.quartz.bean.JobBean;
import com.itjeffrey.autocode.util.SpringContextHolder;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

/**
 * 任务调度上下文
 * 定时任务的创建、修改、停止、恢复、执行、删除
 *
 * <p>
 * JobDetail + Job, 不直接使用Job, JobDetail绑定指定的Job，
 * 每次Scheduler执行一个Job时，先拿到这个Job并创建该Job的实例，再去执行Job中的execute，任务结束时关联的Job实例被释放等待GC清除
 *
 * JobDetail、Trigger都可以使用JobDataMap来设置一些参数或信息。
 *
 * JobExecutionContext中包含了Quartz运行时的环境以及Job本身的详细数据信息。
 * 当Schedule调度执行一个Job的时候，就会将JobExecutionContext传递给该Job的execute，Job就可以通过JobExecutionContext对象获取信息。
 * </p>
 *
 * @From: Jeffrey
 * @Date: 2021/4/16
 */
public class ScheduleContext {

    private static Logger log = LoggerFactory.getLogger(ScheduleContext.class);

    /**
     * Scheduler KEY
     */
    public static String SCHEDULE_KEY = "SCHEDULE_KEY";

    /**
     * Job KEY
     */
    public static JobKey getJobKey(String id) {
        return JobKey.jobKey(SCHEDULE_KEY + id);
    }

    /**
     * Trigger KEY
     */
    public static TriggerKey getTriggerKey(String id) {
        return TriggerKey.triggerKey(SCHEDULE_KEY + id);
    }

    /**
     * 获取表达式触发器
     */
    public static CronTrigger getCronTrigger(Scheduler scheduler, String id) {
        CronTrigger trigger = null;
        try {
            trigger = (CronTrigger) scheduler.getTrigger(getTriggerKey(id));
        } catch (SchedulerException e) {
            log.error("ScheduleContext getCronTrigger error-----CronTrigger get failed----return null---jobId: {}", id);
            e.printStackTrace();
        }
        return trigger;
    }

    /**
     * 创建定时任务
     * scheduler.scheduleJob
     *
     * @param scheduler
     * @param jobBean
     */
    public static void createJob(Scheduler scheduler, JobBean jobBean) {
        try {
            //JobDetail
            JobDetail jobDetail =
                    JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(jobBean.getJobId())).build();
            //CronTrigger
            CronScheduleBuilder cronScheduleBuilder =
                    CronScheduleBuilder.cronSchedule(jobBean.getCron()).withMisfireHandlingInstructionDoNothing();
            CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobBean.getJobId()))
                    .withSchedule(cronScheduleBuilder).build();
            jobDetail.getJobDataMap().put(JobBean.JOB_KEY, jobBean);
            //ScheduleJob
            scheduler.scheduleJob(jobDetail, cronTrigger);
            //If job status is OFF, then pause the job
            if (SysConstant.SCHEDULE_JOB_STS_OFF.equals(jobBean.getStatus())) {
                pauseJob(scheduler, jobBean.getJobId());
            }
        } catch (SchedulerException e) {
            log.error("ScheduleContext createJob error-----scheduler create failed---jobId: {}",
                    jobBean.getJobId());
            e.printStackTrace();
        }

    }

    /**
     * 修改定时任务
     * scheduler.rescheduleJob
     * 修改定时任务实体中的cron表达式，更新Trigger
     *
     * @param scheduler
     * @param jobBean
     */
    public static void updateJob(Scheduler scheduler, JobBean jobBean) {
        try {
            CronTrigger newTrigger =
                    getCronTrigger(scheduler, jobBean.getJobId()).getTriggerBuilder().withIdentity(getTriggerKey(jobBean.getJobId()))
                    .withSchedule(CronScheduleBuilder.cronSchedule(jobBean.getCron()).withMisfireHandlingInstructionDoNothing()).build();
            getCronTrigger(scheduler, jobBean.getJobId()).getJobDataMap().put(JobBean.JOB_KEY, jobBean);
            scheduler.rescheduleJob(getTriggerKey(jobBean.getJobId()), newTrigger);
            //If job status is OFF, then pause job
            if (SysConstant.SCHEDULE_JOB_STS_OFF.equals(jobBean.getStatus())) {
                pauseJob(scheduler, jobBean.getJobId());
            }
        } catch (SchedulerException e) {
            log.error("ScheduleContext updateJob error----Job update failed---jobId: {}", jobBean.getJobId());
            e.printStackTrace();
        }
    }

    /**
     * 暂停定时任务
     *
     * @param scheduler
     * @param id
     */
    public static void pauseJob(Scheduler scheduler, String id) {
        try {
            scheduler.pauseJob(getJobKey(id));
        } catch (SchedulerException e) {
            log.error("ScheduleContext pauseJob error----Job pause failed---jobId: {}", id);
            e.printStackTrace();
        }
    }

    /**
     * 恢复定时任务
     *
     * @param scheduler
     * @param id
     */
    public static void resumeJob(Scheduler scheduler, String id) {
        try {
            scheduler.resumeJob(getJobKey(id));
        } catch (SchedulerException e) {
            log.error("ScheduleContext resumeJob error----Job resume failed---jobId: {}", id);
            e.printStackTrace();
        }
    }

    /**
     * 删除定时任务
     *
     * @param scheduler
     * @param id
     */
    public static void deleteJob(Scheduler scheduler, String id) {
        try {
            scheduler.deleteJob(getJobKey(id));
        } catch (SchedulerException e) {
            log.error("ScheduleContext deleteJob error----Job delete failed---jobId: {}", id);
            e.printStackTrace();
        }
    }

    /**
     * 触发执行定时任务
     *
     * @param scheduler
     * @param jobBean
     */
    public static void run(Scheduler scheduler, JobBean jobBean) {
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put(JobBean.JOB_KEY, jobBean);
        try {
            scheduler.triggerJob(getJobKey(jobBean.getJobId()), jobDataMap);
        } catch (SchedulerException e) {
            log.error("ScheduleContext run job error----Job run failed---jobId: {}", jobBean.getJobId());
            e.printStackTrace();
        }
    }

    /**
     * 查询任务状态
     * @param jobBean
     * @return
     */
    public static Trigger.TriggerState queryJobState(JobBean jobBean){
        try {
//            SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
//            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            Scheduler scheduler = SpringContextHolder.getBean(Scheduler.class);
            TriggerKey triggerKey = TriggerKey.triggerKey(jobBean.getJobName(), jobBean.getGroupId());
            Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
            return triggerState;
        } catch (SchedulerException e) {
            e.printStackTrace();
            log.error("ScheduleContext queryJobState error----Job state get failed---jobName: {}", jobBean.getJobName());
            throw new ACException(ExceptionEnum.JOB_STATE_GET_FAIL);
        }
    }

}
